/*
Designer   : Renyangang

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fpga

/*
#cgo windows LDFLAGS: -luser32
#cgo darwin LDFLAGS: -ldl
#cgo linux LDFLAGS: -ldl

#include <stdlib.h>

#ifdef _WIN32
#include <windows.h>
LPVOID lpMsgBuf = NULL;
#else
#include <dlfcn.h>
#endif

typedef void (*eval)(unsigned char*,int,unsigned char*,int);
typedef void (*init)();
typedef void (*stop)();

// 载入动态库
void* GOLoadLibrary(const char* path) {
    #ifdef _WIN32
        return (void*)LoadLibraryA(path);
    #else
        return dlopen(path, RTLD_LAZY);
    #endif
}

const char* loadError() {
    #ifdef _WIN32
		DWORD dw = GetLastError();
		char* buf = (char*)malloc(50);
		// if (lpMsgBuf) {
		// 	LocalFree(lpMsgBuf);
		// }

		// FormatMessage(
		// 	FORMAT_MESSAGE_ALLOCATE_BUFFER |
		// 	FORMAT_MESSAGE_FROM_SYSTEM |
		// 	FORMAT_MESSAGE_IGNORE_INSERTS,
		// 	NULL,
		// 	dw,
		// 	MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		// 	(LPTSTR) &lpMsgBuf,
		// 	0, NULL );
		printf("Error: %d", dw);
		sprintf(buf,"Error: %d", dw);
		return buf;
    #else
        return dlerror();
    #endif
}

// 获取符号地址
void* GOGetProcAddress(void* handle, const char* symbol) {
    #ifdef _WIN32
        return (void*)GetProcAddress((HMODULE)handle, symbol);
    #else
        return dlsym(handle, symbol);
    #endif
}

void GOEval(void* handle, unsigned char* inputSig,int inputSize,unsigned char* outputSig,int outputSize) {
    ((eval)handle)(inputSig, inputSize, outputSig, outputSize);
}

void GOInit(void* handle) {
    ((init)handle)();
}

void GOStop(void* handle) {
    ((stop)handle)();
}

// 关闭动态库
void GOFreeLibrary(void* handle) {
    #ifdef _WIN32
        FreeLibrary((HMODULE)handle);
    #else
        dlclose(handle);
    #endif
}
*/
import "C"
import (
	"errors"
	"fpga-vboard/logger"
	"unsafe"
)

type DynLibEval struct {
	dynHandler  unsafe.Pointer
	evalFuncPtr unsafe.Pointer
	initFuncPtr unsafe.Pointer
	stopFuncPtr unsafe.Pointer
}

// LoadDynamicLibrary 动态加载库
func LoadDynamicLibrary(path string) (unsafe.Pointer, error) {
	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	handle := C.GOLoadLibrary(cpath)
	if handle == nil {
		return nil, errors.New("failed to load library: " + C.GoString(C.loadError()))
	}
	return handle, nil
}

// GetSymbol 获取符号地址
func GetSymbol(handle unsafe.Pointer, symbol string) (unsafe.Pointer, error) {
	csymbol := C.CString(symbol)
	defer C.free(unsafe.Pointer(csymbol))

	addr := C.GOGetProcAddress(handle, csymbol)
	if addr == nil {
		return nil, errors.New("failed to find symbol")
	}
	return addr, nil
}

// FreeDynamicLibrary 关闭动态库
func FreeDynamicLibrary(handle unsafe.Pointer) {
	C.GOFreeLibrary(handle)
}

func NewDynLibEval(libPath string) (*DynLibEval, error) {
	dl := &DynLibEval{dynHandler: nil, evalFuncPtr: nil}
	err := dl.initFunc(libPath)
	if err != nil {
		logger.ErrorLog("Error initializing library: %v", err)
		return nil, err
	}
	return dl, nil
}

func (dl *DynLibEval) initFunc(libPath string) error {
	if dl.evalFuncPtr == nil {
		var err error
		dl.dynHandler, err = LoadDynamicLibrary(libPath)
		if err != nil {
			logger.ErrorLog("Error loading library: %v", err)
			return err
		}
		dl.evalFuncPtr, err = GetSymbol(dl.dynHandler, "eval")
		if err != nil {
			logger.ErrorLog("Error finding eval symbol: %v", err)
			return err
		}
		dl.initFuncPtr, err = GetSymbol(dl.dynHandler, "init")
		if err != nil {
			logger.ErrorLog("Error finding init symbol: %v", err)
			return err
		}
		dl.stopFuncPtr, err = GetSymbol(dl.dynHandler, "stop")
		if err != nil {
			logger.ErrorLog("Error finding stop symbol: %v", err)
			return err
		}
	}
	return nil
}

func (dl *DynLibEval) Eval(inputSig []byte, outputSig []byte) {
	C.GOEval(dl.evalFuncPtr, (*C.uchar)(unsafe.Pointer(&inputSig[0])), C.int(len(inputSig)), (*C.uchar)(unsafe.Pointer(&outputSig[0])), C.int(len(outputSig)))
}

func (dl *DynLibEval) Init() {
	C.GOInit(dl.initFuncPtr)
}

func (dl *DynLibEval) Stop() {
	C.GOStop(dl.stopFuncPtr)
}

func (dl *DynLibEval) Close() {
	FreeDynamicLibrary(dl.dynHandler)
}
